# -*- coding: utf-8 -*-

# @Project : UI_KDTFrameWork
# @Author  : Mr.Deng
# @Time    : 2021/9/24 22:26

from openpyxl.chart import Reference, PieChart
from openpyxl.styles import Font, Alignment, PatternFill
from openpyxl.utils import get_column_letter

from tools.timeTools import TimeTools
from tools.logConfig import Logger
from tools.operateJson import *

from config.filePathConfig import *

import openpyxl


class CreateReport:

    def __init__(self, startRow: int = 1, startCol: int = 1):
        """
        生成一个测试报告模板，修改起始x，y坐标报告整体移动，所有坐标都以起始位置为基准
        :param startRow: 创建左上角起始坐标行
        :param startCol: 创建左上角起始坐标列
        """

        # 报告模板中字段相对初始化坐标行数值
        self.startRow = startRow
        self.startCol = startCol
        self.descriptionRow = startRow + 2
        self.timeWriteRow = startRow + 3
        self.titleRow = startRow + 4
        self.insertDataRow = startRow + 5
        self.errorLogTitleRow = startRow + 6
        self.caseNameLogRow = startRow + 7
        self.maxRow = 1000
        self.cellHeight = 20
        self.cellWidth = 20

        # 报告单元格底色/文字颜色色号
        self.BLUE = "4498e9"
        self.YELLOW = "f5e9aa"
        self.RED = "FF0000"
        self.GREEN = "00d25e"
        self.PINK = "ff6699"

        # 插入标题固定字段
        self.titleList = [
            "CaseCount", "PassCount", "FailCount",
            "NotRunCount", "PassPercent", "FailPercent"
        ]
        self.LogTitleName = "LogRecord"
        self.CaseName = "CaseName"
        self.CaseLog = "Log"
        self.testResult = "TestResult"
        self.testPass = "Pass"
        self.testFail = "Fail"
        self.runTimeRecord = lambda start, end: f"StartTime：{start}，EndTime：{end}"

        # 创建excel实例
        self.book = openpyxl.Workbook()
        sheet = self.book.active

        # 通过列数获取对应字母
        letterCol = lambda col: get_column_letter(col)

        # 设置报告范围格子，宽高等于 20
        for i in range(startRow, startRow + self.maxRow): sheet.row_dimensions[i].height = self.cellHeight
        for i in range(startCol, len(self.titleList) + startCol): sheet.column_dimensions[
            letterCol(i)].width = self.cellWidth

        # 返回合并单元格格式 例如：A1
        cell = lambda col1, row1: letterCol(col1) + str(row1)

        # 合并单元格，作为标题框，设置底色，字体样式，居中
        sheet.merge_cells(f"{cell(startCol, startRow)}:{cell(len(self.titleList) + startCol - 1, startRow + 1)}")
        sheet.cell(startRow, startCol).fill = fill(self.BLUE)
        sheet.cell(startRow, startCol).font = font(bold=True, size=24)
        sheet.cell(startRow, startCol).alignment = alignment("center", "center")

        # 合并单元格，作为描述框，设置底色，居中
        sheet.merge_cells(
            f"{cell(startCol, self.descriptionRow)}:{cell(len(self.titleList) + startCol - 1, self.descriptionRow)}")
        sheet.cell(self.descriptionRow, startCol).fill = fill(self.YELLOW)
        sheet.cell(self.descriptionRow, startCol).alignment = alignment("center", "center")

        # 合并单元格，作为时间记录框
        sheet.merge_cells(
            f"{cell(startCol, self.timeWriteRow)}:{cell(len(self.titleList) + startCol - 1, self.timeWriteRow)}")
        sheet.cell(self.timeWriteRow, startCol).alignment = alignment("center", "center")
        sheet.cell(self.timeWriteRow, startCol).font = font(bold=True)

        # 写入对应标题列表中的数据
        for col in range(startCol, startCol + len(self.titleList)):
            sheet.cell(self.titleRow, col).value = self.titleList[col - startCol]
            sheet.cell(self.titleRow, col).font = font(bold=True, size=15)

        # 合并单元格，作为错误日志框，设置底色，字体样式，居中
        sheet.merge_cells(
            f"{cell(startCol, self.errorLogTitleRow)}:{cell(len(self.titleList) + startCol - 1, self.errorLogTitleRow)}")
        sheet.cell(self.errorLogTitleRow, startCol).fill = fill(self.PINK)
        sheet.cell(self.errorLogTitleRow, startCol).alignment = alignment("left", "center")
        sheet.cell(self.errorLogTitleRow, startCol).font = font(size=14, bold=True)
        # 添加错误日志标题
        sheet.cell(self.errorLogTitleRow, startCol).value = self.LogTitleName

        # 合并单元格，作为日志记录框，其下所有单元格同步合并
        for row in range(self.caseNameLogRow, self.maxRow):
            sheet.merge_cells(f"{cell(startCol + 2, row)}:{cell(len(self.titleList) + startCol - 1, row)}")
        # 写入标题信息
        sheet.cell(self.caseNameLogRow, startCol).value = self.CaseName
        sheet.cell(self.caseNameLogRow, startCol).font = font(bold=True)
        sheet.cell(self.caseNameLogRow, startCol + 1).value = self.testResult
        sheet.cell(self.caseNameLogRow, startCol + 1).font = font(bold=True)
        sheet.cell(self.caseNameLogRow, startCol + 2).value = self.CaseLog
        sheet.cell(self.caseNameLogRow, startCol + 2).font = font(bold=True)

    def create_pie_chart(self, passCount: int, failCount: int, notRunCount: int, title: str, sheet, place: str):
        """
        生成测试结果饼状图，
        ** 图数据和标签只能从excel表格中取值，而且是列取值，不能横着取值，懒得改报告模板，所以先写入列数据再制图
        :param passCount:
        :param failCount:
        :param notRunCount:
        :param title:
        :param sheet:
        :param place: 报告保存单元格坐标
        :return:
        """
        testData = {"Pass": passCount, "Fail": failCount, "NotRun": notRunCount}

        labelCol = len(self.titleList) + 1
        dataCol = len(self.titleList) + 2

        startRow = self.startRow
        for k, v in testData.items():
            sheet.cell(startRow, labelCol).value = k
            sheet.cell(startRow, dataCol).value = v
            startRow += 1

        pie = PieChart()
        labels = Reference(sheet, min_col=labelCol, min_row=self.startRow, max_row=len(testData))
        data = Reference(sheet, min_col=dataCol, min_row=self.startRow, max_row=len(testData))
        pie.add_data(data)
        pie.set_categories(labels)
        pie.title = title
        sheet.add_chart(pie, place)

    def insert_test_data(self, title, description, startTime, endTime, passCount, failCount, notRunCount,
                         successLog: dict, failLog: dict):
        """
        生成excel测试报告
        :param endTime: 测试结束时间
        :param startTime: 测试开始时间
        :param title: 测试报告名称标题
        :param description: 报告描述
        :param caseCount: 总条数
        :param passCount: 成功条数
        :param failCount: 失败条数
        :param notRunCount: 未执行条数
        :param errorLog: 错误日志字典格式，用力名称：错误日志
        :return:
        """
        sheet = self.book.active

        # 设置报告标题名称
        sheet.cell(self.startRow, self.startCol).value = title
        # 报告描述
        sheet.cell(self.descriptionRow, self.startCol).value = description
        # 测试时间记录
        sheet.cell(self.timeWriteRow, self.startCol).value = self.runTimeRecord(startTime, endTime)

        # 统计数据
        caseCount = passCount + failCount + notRunCount
        sheet.cell(self.insertDataRow, self.startCol + self.titleList.index("CaseCount")).value = caseCount

        sheet.cell(self.insertDataRow, self.startCol + self.titleList.index("PassCount")).value = passCount
        sheet.cell(self.insertDataRow, self.startCol + self.titleList.index("PassCount")).font = font(
            color=self.GREEN)

        sheet.cell(self.insertDataRow, self.startCol + self.titleList.index("FailCount")).value = failCount
        sheet.cell(self.insertDataRow, self.startCol + self.titleList.index("FailCount")).font = font(
            color=self.RED)

        sheet.cell(self.insertDataRow, self.startCol + self.titleList.index("NotRunCount")).value = notRunCount

        # 计算百分比保留两位小数返回字符串
        percent = lambda divisor, dividend: str(format(divisor / dividend * 100, ".2f"))

        sheet.cell(self.insertDataRow, self.startCol + self.titleList.index("PassPercent")).value = \
            percent(passCount, passCount + failCount) + "%"
        sheet.cell(self.insertDataRow, self.startCol + self.titleList.index("PassPercent")).font = font(
            color=self.GREEN)
        sheet.cell(self.insertDataRow, self.startCol + self.titleList.index("FailPercent")).value = \
            percent(failCount, passCount + failCount) + "%"
        sheet.cell(self.insertDataRow, self.startCol + self.titleList.index("FailPercent")).font = font(
            color=self.RED)

        # 写入错误日志信息
        if failLog:
            insertRow = self.caseNameLogRow + 1
            for key, value in failLog.items():
                sheet.cell(insertRow, self.startCol).value = key
                sheet.cell(insertRow, self.startCol).font = font(color=self.RED)
                sheet.cell(insertRow, self.startCol + 1).value = self.testFail
                sheet.cell(insertRow, self.startCol + 1).font = font(color=self.RED, bold=True)
                sheet.cell(insertRow, self.startCol + 2).value = value
                sheet.cell(insertRow, self.startCol + 2).font = font(color=self.RED)
                insertRow += 1
        # 写入正常日志
        if successLog:
            insertRow = self.caseNameLogRow + len(failLog) + 1
            for key, value in successLog.items():
                sheet.cell(insertRow, self.startCol).value = key
                sheet.cell(insertRow, self.startCol + 1).value = self.testPass
                sheet.cell(insertRow, self.startCol + 1).font = font(color=self.GREEN, bold=True)
                sheet.cell(insertRow, self.startCol + 2).value = value
                insertRow += 1

        # 添加饼状图
        addPlace = get_column_letter(len(self.titleList) + 1) + str(self.startRow)
        self.create_pie_chart(passCount=passCount, failCount=failCount, notRunCount=notRunCount, title=title,
                              sheet=sheet, place=addPlace)

        # 保存测试报告
        reportName = reportPath + title + TimeTools.get_now_date("%Y%m%d%H%M%S") + ".xlsx"
        self.book.save(reportName)
        Logger().origin_logger.info(f"生成测试报告保存位置：{reportName}")

    def create_report(self, title, description, startTime, endTime):
        """
        获取测试结果数据写入
        :param title:
        :param description:
        :param startTime:
        :param endTime:
        :return:
        """
        resultData = read_json_data(statisticResultPath)
        successCount = resultData["pass"]["count"]
        failCount = resultData["fail"]["count"]
        notRunCount = resultData["notRun"]
        failLog = resultData["fail"]["errorLog"]
        successLog = resultData["pass"]["log"]

        self.insert_test_data(
            title, description, startTime, endTime, successCount, failCount, notRunCount, successLog, failLog)


def fill(color: str) -> PatternFill:
    """
    设置单元格底色
    :param color:
    :return:
    """
    return PatternFill("solid", fgColor=color)


def alignment(horizontal: str, vertical: str = None) -> Alignment:
    """
    设置单元格上下居中
    :param horizontal: 垂直居中
    :param vertical: 水平居中
    :return:
    """
    return Alignment(horizontal=horizontal, vertical=vertical)


def font(color: str = None, size: int = None, bold: bool = None) -> Font:
    """
    设置字体样式
    :param color: 颜色
    :param size: 大小
    :param bold: 是否加粗
    :return:
    """
    return Font(color=color, size=size, bold=bold)
